feat: add ulims authz policy#310
Conversation
eb36e68 to
6e60748
Compare
There was a problem hiding this comment.
I like the docs that you have put for the policy, We should add a regal lint for this if possible in a separate PR.
Note: Some of the rules depend on token.claims.beamline, I suspect that more generally we would like token.claims.instruments, but I have used beamline for consistency with tiled.rego
I agree we should make the change from beamline to instrument. The faster we do this the better as it will have lesser impact. This needs to be changed from the bundler as well
| # --- Start section copied from tiled.rego --- | ||
|
|
||
| subject := data.diamond.data.subjects[token.claims.fedid] | ||
|
|
||
| # METADATA | ||
| # title: Beamlines | ||
| # description: | | ||
| # Identifies all beamlines the subject is authorized to access | ||
| # based on their assigned permissions. | ||
| beamlines contains beamline if { | ||
| token.claims.fedid | ||
| not admin.is_admin(token.claims.fedid) | ||
| some p in subject.permissions | ||
| some beamline in object.get(data.diamond.data.admin, p, []) | ||
| } | ||
|
|
||
| # Aggregates all session IDs the subject is authorized to view. | ||
| # Admins receive a wildcard "*" granting access to all sessions. | ||
| # Regular users gain session access through three pathways: | ||
| # 1. Direct session membership | ||
| # 2. Access via beamline-level permissions | ||
| # 3. Access via proposal-level permissions | ||
| user_sessions contains "*" if { | ||
| subject | ||
| admin.is_admin(token.claims.fedid) | ||
| } | ||
|
|
||
| user_sessions contains format_int(session, 10) if { | ||
| subject | ||
| not admin.is_admin(token.claims.fedid) | ||
| some session in subject.sessions | ||
| } | ||
|
|
||
| user_sessions contains format_int(session, 10) if { | ||
| subject | ||
| not admin.is_admin(token.claims.fedid) | ||
| some beamline in beamlines | ||
| some session in data.diamond.data.beamlines[beamline].sessions | ||
| } | ||
|
|
||
| user_sessions contains format_int(session, 10) if { | ||
| subject | ||
| not admin.is_admin(token.claims.fedid) | ||
| some p in subject.proposals | ||
| some i in data.diamond.data.proposals[format_int(p, 10)] | ||
| some session in i | ||
| } | ||
|
|
||
| # service account check | ||
| user_sessions contains format_int(session, 10) if { | ||
| not subject | ||
| some session in data.diamond.data.beamlines[token.claims.beamline].sessions | ||
| } | ||
|
|
||
| # --- End section copied from tiled.rego --- |
There was a problem hiding this comment.
We should move this to session because it looks like a common thing rather than application specific.
There was a problem hiding this comment.
beamlines as well, or is that better placed in a separate file?
| # entrypoint: true | ||
| default session_restrictions := [] | ||
|
|
||
| session_restrictions := null if { |
There was a problem hiding this comment.
I personally don't like the "null" restrictions, But I understand what you are trying to do here
|
|
||
| allow if { | ||
| token.verified[0] | ||
| } |
There was a problem hiding this comment.
We can move this to token.rego as valid_token True/False, looks like common policy to me.
Do you make call to OPA to verify your token?
We do this locally in blueapi like this
| filter_sessions contains session if { | ||
| some session in input.instrument_sessions | ||
| proposal_number := format_int(session[0], 10) | ||
| session_number := format_int(session[1], 10) | ||
| format_int(data.diamond.data.proposals[proposal_number].sessions[session_number], 10) in user_sessions | ||
| } |
There was a problem hiding this comment.
| filter_sessions contains session if { | |
| some session in input.instrument_sessions | |
| proposal_number := format_int(session[0], 10) | |
| session_number := format_int(session[1], 10) | |
| format_int(data.diamond.data.proposals[proposal_number].sessions[session_number], 10) in user_sessions | |
| } | |
| filter_sessions contains session if { | |
| "*" not in user_sessions | |
| some session in input.instrument_sessions | |
| proposal_number := format_int(session[0], 10) | |
| session_number := format_int(session[1], 10) | |
| format_int(data.diamond.data.proposals[proposal_number].sessions[session_number], 10) in user_sessions | |
| } |
| # - `input.instruments`, an array of strings representing a list of instruments | ||
| # entrypoint: true | ||
| filter_instruments contains instrument if { | ||
| some instrument in input.instruments |
There was a problem hiding this comment.
not token.claims.beamline
| } | ||
|
|
||
| filter_instruments contains instrument if { | ||
| admin.is_admin(token.claims.fedid) |
There was a problem hiding this comment.
not token.claims.beamline
|
|
||
| filter_instruments contains instrument if { | ||
| admin.is_admin(token.claims.fedid) | ||
| some instrument in input.instruments |
There was a problem hiding this comment.
Should this not check if the instrument is a valid instrument?
If a admin type input.instrument = ["area-51-beamline"] it should return []
| filter_instruments contains instrument if { | ||
| token.claims.beamline | ||
| some instrument in input.instruments | ||
| instrument == token.claims.beamline |
There was a problem hiding this comment.
Same here it should verify the beamline is valid
What about multi-valued claims (i.e. multiple instrument access)? Should I move to |
|
Summary: Adds authZ policy for use in ULIMS services. Essentially, this moves the existing ULIMS policy into the central repository, with the addition of service account support and a new rule for filtering a list of instruments
Added rules:
allow- top-level check that the user has been verifiedsession_restrictions- returns the sessions that the user has access to, in the format[{"beamline": "i03", "proposal_number": 1, "visit_number": 1}, ...]filter_sessions- given an inputinstrument_sessionsof (proposal number, session number) pairs, return those pairs corresponding to session that the user has access tofilter_instruments- given an in putinstruments, a list of instrument names, return those that the user has access to (i.e is an admin of)Note: There is a section of policy that I have copied from
tiled.regoNote: Some of the rules depend on
token.claims.beamline, I suspect that more generally we would liketoken.claims.instruments, but I have usedbeamlinefor consistency withtiled.rego